package se.webbzon.boltzmann.editor.view;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import se.webbzon.boltzmann.editor.mvc.EditorModel;
import se.webbzon.oschi01.worldobject.WorldObject;
import se.webbzon.oschi01.worldobject.WorldStaticObject;

public class WorldViewKeyListener implements KeyListener {
	
	// The editor model associated with this listener
	private final EditorModel model;

	// A map containing all available motions
	private final HashMap<Integer,Action> buttons;

	/*============================================================
	Constructors
	============================================================*/

	public WorldViewKeyListener(EditorModel model) {
		this.model = model;
		buttons = new HashMap<Integer,Action>();
		
		buttons.put(KeyEvent.VK_Q, new UpMotion());
		buttons.put(KeyEvent.VK_E, new DownMotion());
		buttons.put(KeyEvent.VK_A, new LeftMotion());
		buttons.put(KeyEvent.VK_D, new RightMotion());
		buttons.put(KeyEvent.VK_S, new ForwardMotion());
		buttons.put(KeyEvent.VK_W, new BackwardMotion());
		
		buttons.put(KeyEvent.VK_H, new pXScaleMotion());
		buttons.put(KeyEvent.VK_F, new nXScaleMotion());
		buttons.put(KeyEvent.VK_G, new pZScaleMotion());
		buttons.put(KeyEvent.VK_T, new nZScaleMotion());
		buttons.put(KeyEvent.VK_R, new pYScaleMotion());
		buttons.put(KeyEvent.VK_Y, new nYScaleMotion());
		
		buttons.put(KeyEvent.VK_Z, new pRotationMotion());
		buttons.put(KeyEvent.VK_X, new nRotationMotion());
		
		buttons.put(KeyEvent.VK_P, new sizeSnap());
		buttons.put(KeyEvent.VK_O, new positionSnap());
		
		buttons.put(KeyEvent.VK_U, new showAction());
		buttons.put(KeyEvent.VK_I, new hideAction());
		buttons.put(KeyEvent.VK_K, new hardenAction());
		buttons.put(KeyEvent.VK_J, new softenAction());
		
		buttons.put(KeyEvent.VK_SPACE, new focusOnAction());
	}

	/*============================================================
	Public Methods
	============================================================*/

	@Override public void keyPressed(KeyEvent event) {
		Action motion = buttons.get(event.getKeyCode());
		if (motion != null) {
			final ArrayList<WorldStaticObject> instances = 
					new ArrayList<WorldStaticObject>();
			for (WorldObject instance : model.getSelectedInstances())
				if (instance instanceof WorldStaticObject)
					instances.add((WorldStaticObject) instance);

			motion.performAction(instances);
			
			for (WorldObject instance : instances)
				model.changeInstance(this,instance);
		}
	}

	@Override public void keyReleased(KeyEvent event) {};

	@Override public void keyTyped(KeyEvent event) {};
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** An abstract action class. **/
	private static abstract class Action {
		public abstract void performAction(Collection<WorldStaticObject> instances);
	}
	
	/** An up action implementation. **/
	private final class UpMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setPosition(	instance.getX(), 
										instance.getY() + model.getGrid().getHeight(), 
										instance.getZ());
		}
	}
	
	/** A down action implementation. **/
	private final class DownMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setPosition(	instance.getX(), 
										instance.getY() - model.getGrid().getHeight(), 
										instance.getZ());
		}
	}
	
	/** A left action implementation. **/
	private final class LeftMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setPosition(	instance.getX() - model.getGrid().getWidth(), 
										instance.getY(), 
										instance.getZ());
		}
	}
	
	/** A right action implementation. **/
	private final class RightMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setPosition(	instance.getX() + model.getGrid().getWidth(), 
										instance.getY(), 
										instance.getZ());
		}
	}
	
	/** A forward action implementation. **/
	private final class ForwardMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setPosition(	instance.getX(), 
										instance.getY(), 
										instance.getZ() + model.getGrid().getDepth());
		}
	}
	
	/** A backward action implementation. **/
	private final class BackwardMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setPosition(	instance.getX(), 
										instance.getY(), 
										instance.getZ() - model.getGrid().getDepth());
		}
	}
	
	/** A positive x-scale action implementation. **/
	private final class pXScaleMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSize(	instance.getWidth() + model.getGrid().getWidth()/2,
									instance.getHeight(),
									instance.getDepth());
		}
	}
	
	/** A negative x-scale action implementation. **/
	private final class nXScaleMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSize(	instance.getWidth() - model.getGrid().getWidth()/2,
									instance.getHeight(),
									instance.getDepth());
		}
	}

	/** A positive y-scale action implementation. **/
	private final class pYScaleMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSize(	instance.getWidth(),
									instance.getHeight() + model.getGrid().getHeight()/2,
									instance.getDepth());
		}
	}
	
	/** A negative y-scale action implementation. **/
	private final class nYScaleMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSize(	instance.getWidth(),
									instance.getHeight() - model.getGrid().getHeight()/2,
									instance.getDepth());
		}
	}

	/** A positive z-scale action implementation. **/
	private final class pZScaleMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSize(	instance.getWidth(),
									instance.getHeight(),
									instance.getDepth() + model.getGrid().getDepth()/2);
		}
	}
	
	/** A negative z-scale action implementation. **/
	private final class nZScaleMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSize(	instance.getWidth(),
									instance.getHeight(),
									instance.getDepth() - model.getGrid().getDepth()/2);
		}
	}
	
	/** A positive rotation action implementation. **/
	private final class pRotationMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setRotation(	instance.getEX(),
										instance.getEY(),
										instance.getEZ(),
										instance.getAngle() + Math.PI/18);
		}
	}
	
	/** A negative rotation action implementation. **/
	private final class nRotationMotion extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setRotation(	instance.getEX(),
										instance.getEY(),
										instance.getEZ(),
										instance.getAngle() - Math.PI/18);
		}
	}
	
	/** A size snap action implementation. **/
	private final class sizeSnap extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			final double gw = model.getGrid().getWidth();
			final double gh = model.getGrid().getHeight();
			final double gd = model.getGrid().getDepth();
			for (WorldStaticObject instance : instances) {
				instance.setSize(	gw * Math.ceil(instance.getWidth() / gw),
									gh * Math.ceil(instance.getHeight() / gh),
									gd * Math.ceil(instance.getDepth() / gd));
			}
		}
	}
	
	/** A position snap action implementation. **/
	private final class positionSnap extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			final double gw = model.getGrid().getWidth();
			final double gh = model.getGrid().getHeight();
			final double gd = model.getGrid().getDepth();
			for (WorldStaticObject instance : instances) {
				instance.setPosition(	gw * Math.ceil(instance.getX() / gw),
										gh * Math.ceil(instance.getY() / gh),
										gd * Math.ceil(instance.getZ() / gd));
			}
		}
	}
	
	/** A show instance action implementation. **/
	private final class showAction extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setVisible(true);
		}
		
	}
	
	/** An hide instance action implementation. **/
	private final class hideAction extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setVisible(false);
		}
		
	}
	
	/** An harden instance action implementation. **/
	private final class hardenAction extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSolid(true);
		}
		
	}
	
	/** A soften instance action implementation. **/
	private final class softenAction extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			for (WorldStaticObject instance : instances)
				instance.setSolid(false);
		}
		
	}
	
	/** A focus on instance action implementation. **/
	private final class focusOnAction extends Action {
		@Override public void performAction(Collection<WorldStaticObject> instances) {
			if (!instances.isEmpty()) {
				WorldStaticObject instance = instances.iterator().next();
				model.focusOnInstance(this,instance);
				new HighlightThread(instance).start();
			}
		}
	}
	
	/** An instance highlight thread. **/
	private final class HighlightThread extends Thread {
		private final WorldStaticObject instance;
		private static final int highlights = 6;
		public HighlightThread(WorldStaticObject instance) {
			this.instance = instance;
		}
		@Override public void run() {
			for (int i = 0; i < highlights; i++) {
				instance.setVisible(!instance.isVisible());
				try {
					Thread.sleep(150);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

}
